package com.example.framework509.common.services.Impl;

import com.example.framework509.common.entities.MapperParam;
import com.example.framework509.common.exceptions.ParamFieldException;
import com.example.framework509.common.exceptions.JdbcException;
import com.example.framework509.common.services.OneToManyMapperService;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class OneToManyAutoMapperImpl<E, PRIMARY_CLASS, SECONDARY_CLASS> extends AutoMapperServiceImpl<E, PRIMARY_CLASS, SECONDARY_CLASS> implements OneToManyMapperService<E> {

    @Override
    public void addMapperInRedis(MapperParam param) {
        redisUtil.sSet(ENTITY_NAME_LOWER + PRIMARY_FIELD_NAME + ":" + param.getPrimaryId(), param.getSecondaryId());
        redisUtil.set(ENTITY_NAME_LOWER + SECONDARY_FIELD_NAME + ":" + param.getSecondaryId(), param.getPrimaryId());
    }

    @Override
    public void delMapperInRedis(MapperParam param) {
        redisUtil.setRemove(ENTITY_NAME_LOWER + PRIMARY_FIELD_NAME + ":" + param.getPrimaryId(), param.getSecondaryId());
        redisUtil.del(ENTITY_NAME_LOWER + SECONDARY_FIELD_NAME + ":" + param.getSecondaryId());
    }

    @Override
    protected boolean hasSameMapper(Integer primaryId, Integer secondaryId) {
        Object obj = redisUtil.get(ENTITY_NAME_LOWER + SECONDARY_FIELD_NAME + ":" + secondaryId);
        if (obj != null && obj.toString().equals(primaryId.toString()))
            return true;
        Integer primaryIdFromSecondaryId = getPrimaryIdFromSecondaryId(secondaryId);
        if (primaryIdFromSecondaryId!=null&&!primaryIdFromSecondaryId.equals(primaryId))
            throw new JdbcException("One to Many relation not matched");
        return false;
    }

    @Override
    public Set<Integer> getSecondaryIdsFromPrimaryId(Integer primaryId) {
        getAllEntityNum();

        Set<Object> res = redisUtil.sGet(ENTITY_NAME_LOWER + PRIMARY_FIELD_NAME + ":" + primaryId);
        Set<Integer> childrenIds = new HashSet<>();
        if (res == null || res.size() == 0) {
            return new HashSet<>();
        } else {
            for (Object idStr : res) {
                int idInt = Integer.parseInt((idStr).toString());
                childrenIds.add(idInt);
            }
        }
        return childrenIds;
    }


    @Override
    public Integer getPrimaryIdFromSecondaryId(Integer secondaryId) {
        getAllEntityNum();

        Object obj = redisUtil.get(ENTITY_NAME_LOWER + SECONDARY_FIELD_NAME + ":" + secondaryId);
        if (obj == null) {
            return null;
        }
        return Integer.parseInt(obj.toString());
    }

    public PRIMARY_CLASS getPrimaryEntityFromSecondaryId(Integer secondaryId){
        Integer primaryIdFromSecondaryId = getPrimaryIdFromSecondaryId(secondaryId);
        PRIMARY_CLASS entityFromId = primaryEntityBaseService.getEntityFromId(primaryIdFromSecondaryId);
        return entityFromId;
    }

    public List<SECONDARY_CLASS> getSecondaryEntityFromPrimaryId(Integer primaryId){
        Set<Integer> secondaryIdsFromPrimaryId = getSecondaryIdsFromPrimaryId(primaryId);
        List<SECONDARY_CLASS> entities = new ArrayList<>();
        for (Integer id:secondaryIdsFromPrimaryId){
            entities.add(secondaryEntityBaseService.getEntityFromId(id));
        }
        return entities;
    }

    @Override
    protected void editSecFetchCallBack(Integer primaryId, List<Integer> secondaryIds) throws Exception {
        Set<Integer> secondaryIdsFromPrimaryId = getSecondaryIdsFromPrimaryId(primaryId);
        List<Integer> secondaryIdsFromDB = new ArrayList<>(secondaryIdsFromPrimaryId);
        List<Integer> delIds = computeSubtraction(secondaryIdsFromDB, secondaryIds);
        delMapperFetch(primaryId,delIds);
        List<Integer> addIds = computeSubtraction(secondaryIds, secondaryIdsFromDB);
        addMapperFetch(primaryId,addIds);
    }

    @Override
    protected void editPriFetchCallBack(Integer secondaryId, List<Integer> primaryIds) throws Exception {
        if(primaryIds.size()!=1) throw new ParamFieldException("映射关系数量异常");
        Integer primaryIdFromSecondaryId = getPrimaryIdFromSecondaryId(secondaryId);
        delMapper(new MapperParam(primaryIdFromSecondaryId,secondaryId));
        addMapper(new MapperParam(primaryIds.get(0),secondaryId));
    }
}
